﻿using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks;
using Internal.Runtime.Services.Battle;
using KuiHuaBaoDian;
using KuiHuaBaoDian.Services.Asset;
using Runtime.Services.Battle.Entity;
using UnityEngine;
using UnityEngine.Pool;

namespace Runtime.Services.Battle {

    public abstract class DFR_BattleEntityRecordBase : IDFR_BattleEntityRecordBaseInternal {

        private static readonly Dictionary<string, string[]> m_SplitCacheDictionary = new();
        private static readonly HashSet<DFR_BattleEntityInstanceID> m_DisposedSet = new();

        private static readonly ArrayPool<UniTask<GameObject>> Task2ArrayPool = ArrayPool<UniTask<GameObject>>.Create(2, 64);
        private static readonly ArrayPool<UniTask<GameObject>> Task4ArrayPool = ArrayPool<UniTask<GameObject>>.Create(4, 64);
        private static readonly Dictionary<int, ArrayPool<UniTask<GameObject>>> TaskArrayPoolDictionary = new() {
                { 2, Task2ArrayPool },
                { 4, Task4ArrayPool },
            };

        private static Transform m_CacheTransform;

        internal static void Clear() {
            m_DisposedSet.Clear();
        }

        internal void InternalAllocate() => Allocate();
        protected virtual void Allocate() {
            IsReleased = false;
            if (m_CacheTransform == null) {
                var gameObject = new GameObject(nameof(DFR_BattleEntityRecordBase));
                UnityEngine.Object.DontDestroyOnLoad(gameObject);
                gameObject.hideFlags = HideFlags.HideAndDontSave;
                m_CacheTransform = gameObject.transform;
            }
            SpeederSet = HashSetPool<IDFR_BattleSpeeder>.Get();
            CancellationTokenSource = new();
            m_SpriteRendererIsInitialized = false;
            m_SpriteRenderersIsInitialized = false;
            m_IsModelAssetLoaded = false;
        }

        public IDFR_BattleEntityBase Entity { get; private set; }

        public DFR_BattleEntityHandle Handle { get; private set; }

        public GameObject Container { get; private set; }

        public GameObject Transformer { get; private set; }

        public GameObject ModelContainer { get; private set; }

        public IDFR_BattleEntityAssetVehicleInternal StateVehicle { get; private set; }

        public IDFR_BattleEntityAssetVehicleInternal SFXVehicle { get; private set; }

        protected abstract int Layer { get; }

        protected CancellationTokenSource CancellationTokenSource { get; private set; }

        protected HashSet<IDFR_BattleSpeeder> SpeederSet { get; private set; }

        private bool m_IsModelAssetLoaded;

        protected GameObject ModelAsset => m_ModelAsset;
        private GameObject m_ModelAsset;
        private GameObject[] m_ModelAssets;

        private GameObject m_SFXAsset;

        private bool m_SpriteRendererIsInitialized;
        private SpriteRenderer m_SpriteRenderer;
        private bool m_SpriteRenderersIsInitialized;
        private SpriteRenderer[] m_SpriteRenderers;
        protected async UniTask<SpriteRenderer> GetSpriteRenderer(DFR_BattleEntityInstanceID entityInstanceID) {
            if (!m_SpriteRendererIsInitialized && m_SpriteRenderer == null && !m_DisposedSet.Contains(entityInstanceID)) {
                if (m_ModelAsset == null) {
                    await KHBD.Context.WaitUntil(() => m_IsModelAssetLoaded, CancellationTokenSource.Token);
                }
                if (m_ModelAsset != null) {
                    m_SpriteRenderer = m_ModelAsset.GetComponentInChildren<SpriteRenderer>();
                }
                if (!CancellationTokenSource.IsCancellationRequested) {
                    m_SpriteRendererIsInitialized = true;
                }
            }
            return m_SpriteRenderer;
        }
        protected async UniTask<SpriteRenderer[]> GetSpriteRenderers(DFR_BattleEntityInstanceID entityInstanceID) {
            if (!m_SpriteRenderersIsInitialized && m_SpriteRenderers == null && !m_DisposedSet.Contains(entityInstanceID)) {
                if (m_ModelAsset == null) {
                    await KHBD.Context.WaitUntil(() => m_IsModelAssetLoaded, CancellationTokenSource.Token);
                }
                if (m_ModelAsset != null) {
                    m_SpriteRenderers = m_ModelAsset.GetComponentsInChildren<SpriteRenderer>();
                }
                if (!CancellationTokenSource.IsCancellationRequested) {
                    m_SpriteRenderersIsInitialized = true;
                }
            }
            return m_SpriteRenderers;
        }

        void IDFR_BattleEntityRecordBaseInternal.Initialize(IDFR_BattleEntityBase entity) {
            Entity = entity;
        }

        private float m_LocalPositionZ;

        public void Placehold<TStateVehicle, TSFXVehicle>(DFR_BattleEntityHandle handle, Transform parent, Vector3 worldPosition, float localZ, string entityDebugName) where TStateVehicle : UnityEngine.Component, IDFR_BattleEntityAssetVehicle where TSFXVehicle : UnityEngine.Component, IDFR_BattleEntityAssetVehicle {

            // String.Format有一定开销
#if UNITY_EDITOR
            var entityName = $"{(string.IsNullOrWhiteSpace(entityDebugName) ? "EntityContainer" : entityDebugName)} [{handle.EntityInstanceID.ID.Type}_{handle.EntityInstanceID.ID.TableID}({handle.EntityInstanceID.GUID})]{handle.LockstepEntityHandle}";
#else
            var entityName = "EntityContainer";
#endif
            if (Container == null) {
                var container = new GameObject(entityName) {
                    layer = Layer
                };
                container.transform.SetParent(parent);
                container.transform.ResetLocalMatrix();
                Container = container;

                var transformer = new GameObject("EntityTransformer") {
                    layer = Layer,
                };
                transformer.transform.SetParent(container.transform);
                transformer.transform.ResetLocalMatrix();
                Transformer = transformer;

                ModelContainer = new GameObject(nameof(ModelContainer)) {
                    layer = Layer,
                };
                ModelContainer.transform.SetParent(Transformer.transform);
                ModelContainer.transform.ResetLocalMatrix();
            } else {
#if UNITY_EDITOR
                Container.name = entityName;
#endif
                Container.transform.SetParent(parent);
                Container.transform.ResetLocalMatrix();
                Transformer.transform.ResetLocalMatrix();
                ModelContainer.transform.ResetLocalMatrix();
                Container.SetActive(true);
            }

            Container.transform.localPosition = worldPosition + new Vector3(BattleServiceBase.Instance.CellWidth / 2f, BattleServiceBase.Instance.CellHeight / 2f, m_LocalPositionZ);
            m_LocalPositionZ = localZ;

            if (!Container.TryGetComponent<TStateVehicle>(out var stateVehicle)) {
                stateVehicle = Container.AddComponent<TStateVehicle>();
            }
            StateVehicle = stateVehicle as IDFR_BattleEntityAssetVehicleInternal;

            if (!Container.TryGetComponent<TSFXVehicle>(out var sfxVehicle)) {
                sfxVehicle = Container.AddComponent<TSFXVehicle>();
            }
            SFXVehicle = sfxVehicle as IDFR_BattleEntityAssetVehicleInternal;
        }

        public void Move(Vector3 worldPosition) {
            Container.transform.localPosition = worldPosition + new Vector3(BattleServiceBase.Instance.CellWidth / 2f, BattleServiceBase.Instance.CellHeight / 2f, m_LocalPositionZ);
        }

        public void RefreshTimeScale() {
            foreach (var speeder in SpeederSet) {
                speeder.SetSpeed(DFR_BattleBase.DataCenter.Models.TimeScale.Value);
            }
        }

        public async UniTaskVoid InstantiateModels(DFR_BattleEntityInstanceID entityInstanceID, string[] assetKeys, int assetKeyLength, Action onComplete = null, string cacheGroup = null) {
            UnityEngine.Assertions.Assert.IsNotNull(assetKeys);
            UnityEngine.Assertions.Assert.IsTrue(assetKeys.Length > 0);

            var taskPool = TaskArrayPoolDictionary[assetKeyLength];
            var taskRented = taskPool.Rent(assetKeyLength);
            for (var i = 0; i < assetKeyLength; i++) {
                var assetKey = assetKeys[i];
                var task = DoInstantiate(entityInstanceID, assetKey, ModelContainer.transform, cacheGroup: cacheGroup);
                taskRented[i] = task;
            }
            var assets = await UniTask.WhenAll(taskRented);
            taskPool.Return(taskRented, true);
            onComplete?.Invoke();
            if (assets == null || assets.Take(assetKeyLength).Any(item => item == null)) {
                return;
            }
            if (StateVehicle != null) {
                m_ModelAssets = assets;
                StateVehicle.OnLoaded(m_ModelAssets);
            }
        }

        public async UniTaskVoid InstantiateModel(DFR_BattleEntityInstanceID entityInstanceID, string assetKey, string cacheGroup = null) {
            m_ModelAsset = await DoInstantiate(entityInstanceID, assetKey, ModelContainer.transform, cacheGroup: cacheGroup);
            if (StateVehicle != null && m_ModelAsset != null) {
                StateVehicle.OnLoaded(m_ModelAsset);
            }
            m_IsModelAssetLoaded = true;
        }

        public async UniTaskVoid InstantiateSFX(DFR_BattleEntityInstanceID entityInstanceID) {
            m_SFXAsset = await DoInstantiate(entityInstanceID, $"model_model_sfx", Container.transform);
            if (SFXVehicle != null && m_SFXAsset != null) {
                SFXVehicle.OnLoaded(m_SFXAsset);
            }
        }

        protected async UniTask<GameObject> DoInstantiate(DFR_BattleEntityInstanceID entityInstanceID, string assetKey, Transform parent = null, bool isActive = true, string cacheGroup = null) {
            if (m_DisposedSet.Contains(entityInstanceID)) {
                return null;
            }

            if (string.IsNullOrWhiteSpace(assetKey)) {
                assetKey = "model_model_empty";
            }

            // 不缓存的话，数量级大的情况下，有明显CPU消耗
            if (!m_SplitCacheDictionary.TryGetValue(assetKey, out var keys)) {
                keys = m_SplitCacheDictionary[assetKey] = assetKey.Split("_");
            }
            var groupPrefix = keys[0];
            if (string.IsNullOrEmpty(keys[1])) {
                assetKey = "model_model_empty";
            }

            if (parent == null) {
                parent = Transformer.transform;
            }
            var asset = await AssetService.Instance.Instantiate(AssetRequest.Instantiate(assetKey, CancellationTokenSource.Token), parent, false, cacheGroup: cacheGroup);

            if (m_DisposedSet.Contains(entityInstanceID)) {
                if (asset != null) {
                    AssetService.Instance.Release(asset);
                }
                return null;
            }

            if (asset == null && CancellationTokenSource != null && !CancellationTokenSource.IsCancellationRequested) {
                asset = await InstantiateDefault(parent, groupPrefix);
            }
            if (asset == null) {
                return null;
            }

            if (m_DisposedSet.Contains(entityInstanceID)) {
                if (asset != null) {
                    AssetService.Instance.Release(asset);
                }
                return null;
            }

            asset.transform.ResetLocalMatrix();
            asset.transform.localScale = new Vector3(BattleServiceBase.Instance.CellWidth, BattleServiceBase.Instance.CellHeight, 1f);
            asset.SetLayerRecursively(Layer);
            if (isActive) {
                asset.SetActive(true);
            }
            return asset;
        }

        private async UniTask<GameObject> InstantiateDefault(Transform parent, string groupPrefix = null) {
            var hasDefault = true;
            var i = 0;
            while (i++ < 100) {
                var asset = await InstantiateDefault(parent, hasDefault, groupPrefix);
                if (asset != null) {
                    return asset;
                } else {
                    hasDefault = false;
                }
            }
            UnityEngine.Debug.LogWarning("这个分类没有找到可作默认模型的资产");
            return null;
        }

        protected abstract UniTask<GameObject> InstantiateDefault(Transform parent, bool isLastValid, string prefix = null);

        public void Done(DFR_BattleEntityHandle handle) => Handle = handle;

        protected void ProcessParticleSystem(GameObject asset, Action onRelease = null) {
            var particleSystems = asset.GetComponentsInChildren<ParticleSystem>();
            if (particleSystems.Length > 0) {
                var speeder = asset.AddComponent<DFR_BattleParticleSystemSpeeder>();
                speeder.SetSpeed(DFR_BattleBase.DataCenter.Models.TimeScale.Value);
                SpeederSet.Add(speeder);
                var doneCount = 0;
                foreach (var particleSystem in particleSystems) {
                    var listener = particleSystem.gameObject.AddComponent<ParticleSystemListener>();
                    listener.onComplete.AddListener(() => {
                        doneCount++;
                        if (doneCount >= particleSystems.Length && asset != null) {
                            SpeederSet?.Remove(speeder);
                            AssetService.Release(asset);
                            onRelease?.Invoke();
                        }
                    });
                }
            } else {
                AssetService.Release(asset);
                onRelease?.Invoke();
            }
        }

        protected void ClearParticleSystem(GameObject asset) {
            var particleSystems = asset.GetComponentsInChildren<ParticleSystem>();
            if (particleSystems.Length > 0 && asset.TryGetComponent<DFR_BattleParticleSystemSpeeder>(out var speeder)) {
                SpeederSet?.Remove(speeder);
                foreach (var particleSystem in particleSystems) {
                    if (particleSystem.gameObject.TryGetComponent<ParticleSystemListener>(out var listener)) {
                        listener.onComplete.RemoveAllListeners();
                    }
                }
            }
        }

        public bool IsReleased { get; private set; }
        internal void InternalRelease() => Release();
        protected virtual void Release() {
            if (IsReleased) {
                return;
            }
            IsReleased = true;

            m_DisposedSet.Add(Handle.EntityInstanceID);

            m_SpriteRenderer = null;
            m_SpriteRenderers = null;

            if (m_ModelAsset != null) {
                AssetService.Release(m_ModelAsset);
                m_ModelAsset = null;
            } else if (m_ModelAssets != null) {
                foreach (var asset in m_ModelAssets) {
                    if (asset != null) {
                        AssetService.Release(asset);
                    }
                }
                m_ModelAssets = null;
            } else {
                CancellationTokenSource.Cancel();
            }
            CancellationTokenSource = null;

            if (m_SFXAsset != null) {
                AssetService.Release(m_SFXAsset);
                m_SFXAsset = null;
            }
            //if (Container != null) {
            //    UnityEngine.Object.Destroy(ModelContainer);
            //    ModelContainer = null;
            //    UnityEngine.Object.Destroy(Transformer);
            //    Transformer = null;
            //    UnityEngine.Object.Destroy(Container);
            //    Container = null;
            //}
            if (Entity is IDisposable disposableEntity) {
                disposableEntity.Dispose();
                Entity = null;
            }

            if (StateVehicle is IDisposable disposableStateVehicle) {
                disposableStateVehicle.Dispose();
                StateVehicle = null;
            }

            if (SFXVehicle is IDisposable disposableSFXVehicle) {
                disposableSFXVehicle.Dispose();
                SFXVehicle = null;
            }

            if (SpeederSet != null) {
                HashSetPool<IDFR_BattleSpeeder>.Release(SpeederSet);
                SpeederSet = null;
            }

            if (Container != null) {
                Container.SetActive(false);
                Container.transform.SetParent(m_CacheTransform);
            }
        }
    }
}

namespace Internal.Runtime.Services.Battle {

    public interface IDFR_BattleEntityRecordBaseInternal {

        void Initialize(IDFR_BattleEntityBase entity);
    }
}